<template>
  <code-modal :title="$t('formSetting.jsCodeBlock.jsCodeTest')" :show.sync="visible" :allow-auto-close="false" width="100vw" class="compilation-test-modal" @close="onClose" v-if="visible" :is-show-radius="true">
    <template #operate>
      <header>
        <div @click="testRun">
          <i class="iconfont icon-huojian" style="font-size:12px;"></i>
          <span>{{$t('common.form.tip.jsCodeBlock.debug')}}</span>
        </div>
        <div @click="handleSave">
          <i class="iconfont icon-save" style="font-size:12px;"></i>
          <span>{{ $t('common.base.save') }}</span>
        </div>
      </header>
    </template>
    <div class="compilation-test-modal__content">
      <main>
        <section class="code-editor-left">
          <cm-editor @handleField="handleField" :can-inserted-fields="canInsertedFields"></cm-editor>
        </section>
        <section class="code-editor-right">
          <section class="code-editor-right-top" >
            <codemirror
              class="codemirror"
              ref="cmEditor"
              v-model="codeBlockConfig.codeContent"
              :options="codeMirrorOption"
              @changes="handleCodemirrorChange"
            />
          </section>
          <footer>
            <div class="console-panel">
              <p>{{ $t('common.form.tip.jsCodeBlock.debug') }}</p>
              <div class="alias-btn" v-if="isSuccess && resultAliasPath.length">
                <el-button type="primary" icon="el-icon-edit" size="mini" @click="handleCreateAlias">{{ $t('common.form.tip.jsCodeBlock.createAlias') }}</el-button>
              </div>
            </div>
            <section class="test-run-right">
              <section class="test-run-inforn">
                <section class="test-run-content" v-if="runResults">
                  <pre v-html="runResults" class="no-base-tip" v-if="isSuccess"></pre>
                  <p class="test-run-error" v-else> Error: {{ runResults }}</p>
                </section>
              </section>
            </section>
          </footer>
        </section>
      </main>
      <test-run-modal @testCompilerResult="testCompilerResult" :fields="insertFields" :anaclysis-results="analysisResults" ref="testRunModals"></test-run-modal>
      <return-parse-modal :fields="insertFields" :anaclysis-results="analysisResults" :result-alias-path="resultAliasPath" @submitAliasValue="outResultAliasPath" ref="returnParseModal"></return-parse-modal>
    </div>
  </code-modal>
</template>
<script>
import codeModal from '@src/component/form/components/FormJsControl/components/codeModal.vue';
import cmEditor from './cmEditor';
import testRunModal from "./testRunModal";
import ReturnParseModal from "@src/component/form/components/FormJsControl/components/compileEditor/returnParseModal.vue";
//@ts-ignore
import { codemirror } from 'vue-codemirror';
// 引入语法
import 'codemirror/mode/javascript/javascript.js';
// 引入主题
import 'codemirror/theme/base16-dark.css';
import 'codemirror/addon/scroll/simplescrollbars.css';
import 'codemirror/addon/scroll/simplescrollbars';

import {checkJsCode, isObjectString,executeCode,syntaxHighlight} from '../../model/index';

import _, {cloneDeep, isString} from 'lodash';
import {CustomerFieldNameMappingEnum, FieldTypeMappingEnum, TaskFieldNameMappingEnum} from "@model/enum/FieldMappingEnum";
// import sysFields from "@src/view/template/list/util/sysFields";
import platform from "@src/platform";
import i18n from "@src/locales";
import { isInSubForm } from '@src/component/form/util/index';
import { uuid } from  'pub-bbx-utils';


const SUPPORT_INSER_TEDFIELD = [

  TaskFieldNameMappingEnum.TaskNo,
  TaskFieldNameMappingEnum.PlanStartTime,
  TaskFieldNameMappingEnum.PlanEndTime,
  TaskFieldNameMappingEnum.EventNo,
  TaskFieldNameMappingEnum.State,

  FieldTypeMappingEnum.Text,
  FieldTypeMappingEnum.Textarea,
  FieldTypeMappingEnum.Number,
  FieldTypeMappingEnum.Select,
  FieldTypeMappingEnum.Cascader,
  FieldTypeMappingEnum.User,
  FieldTypeMappingEnum.Date,
  FieldTypeMappingEnum.Phone,
  FieldTypeMappingEnum.Email,
  FieldTypeMappingEnum.Address,
  FieldTypeMappingEnum.Location,
  FieldTypeMappingEnum.Formula,
  // FieldTypeMappingEnum.SubForm,
  // FieldTypeMappingEnum.SparePart,
  // FieldTypeMappingEnum.ServiceItem,
  // FieldTypeMappingEnum.MaterialOrder,
  // FieldTypeMappingEnum.Pay,
  // FieldTypeMappingEnum.RelatedTask,
  CustomerFieldNameMappingEnum.Customer,
  // FieldTypeMappingEnum.Product,
  // CustomerFieldNameMappingEnum.CustomerAddress,
  CustomerFieldNameMappingEnum.Linkman,
  // FieldTypeMappingEnum.OutWarehouse,
  // FieldTypeMappingEnum.InWarehouse,
  // FieldTypeMappingEnum.InSparepart,
  // FieldTypeMappingEnum.OutSparepart,
  // FieldTypeMappingEnum.CurrencyCode
];
export default {
  name:'compilation-test-dialog',
  components: {
    [ReturnParseModal.name]:ReturnParseModal,
    [codeModal.name]:codeModal,
    [cmEditor.name]:cmEditor,
    codemirror,
    [testRunModal.name]:testRunModal
  },
  props: {
    field: {
      type: Object,
      default: () => ({})
    },
    fields: {
      type: Array,
      default: () => ([])
    },
  },
  data() {
    return {
      visible: false,
      pending: true,
      show:false,
      active: 0,
      analysisResults: '',
      stepData: [this.$t('formSetting.jsCodeBlock.coding'), this.$t('formSetting.jsCodeBlock.testRun'), this.$t('formSetting.jsCodeBlock.returnParsing')],
      insertFields: [],
      codeRunResult:'',
      codeBlockConfig: {
        automaticOperation: 0,
        codeContent:'', // js代码块
        resultAliasPath:[], // 创建别名
      },
      codeMirrorOption : {
        lineWrapping: true,
        autoCloseBrackets: true,
        tabSize: 4,
        styleActiveLine: true,
        lineNumbers: true,
        matchBrackets: true,
        line: true,
        autoRefresh: true,
        theme: 'base16-dark',
        foldGutter: true,  // 可将对象折叠，与下面的gutters一起使用
        extraKeys: { "Alt-/": "autocomplete" }, //自动补全
        mode: 'text/javascript',
        scrollbarStyle: 'overlay',
        hintOptions: {// 自定义提示选项
          completeSingle: false
        },
        // 自定义特殊占位符，无效文字会显示一个Unicode编码的span元素
        specialCharPlaceholder: function (ch) {
          let token = document.createElement("span");
          let content = "";
          token.className = "cm-invalidchar";
          if (typeof content == "string") {
            token.appendChild(document.createTextNode(content));
          }
          token.title = "\\u" + ch.charCodeAt(0).toString(16);
          token.setAttribute("aria-label", token.title);
          return token;
        },
        matchTags: { bothTags: true },  // 将突出显示光标周围的标签
        gutters: [
          "CodeMirror-linenumbers",
          "CodeMirror-foldgutter",
          "CodeMirror-lint-markers",      // CodeMirror-lint-markers是实现语法报错功能
        ],
      }
    };
  },
  watch: {
    visible(newValue) {
      if(newValue) {
        // 初始化数据
        const { codeBlockConfig = {} } = this.field.setting;
        const code = codeBlockConfig?.codeContent || '';
        if(code) {
          this.codeBlockConfig = _.cloneDeep(codeBlockConfig);
          this.insertFields = this.getInsertFields(code);
          this.codeRunResult = '';
        } else {
          this.codeBlockConfig.codeContent = '';
        }
        this.active = 0;
      }
    },
  },
  computed: {
    runResults() {
      if(this.isSuccess) return syntaxHighlight(this.codeRunResult);
      return this.codeRunResult;
    },
    isSuccess() {
      return isObjectString(this.codeRunResult);
    },
    /* 能插入的表单字段别名 */
    resultAliasPath() {
      return this.codeBlockConfig?.resultAliasPath || [];
    },
    /* 能插入的表单字段 */
    canInsertedFields() {
      if (isInSubForm(this.field)) {
        const parentFields = this.fields.find(field => field.fieldName === this.field.parentFieldName);
        const subFormFieldList = parentFields?.subFormFieldList || [];
        return subFormFieldList.filter(
          field => SUPPORT_INSER_TEDFIELD.includes(field.formType)
        );
      } else {
        return this.fields.filter(
          field => SUPPORT_INSER_TEDFIELD.includes(field.formType)
        );
      }
    },
  },
  methods:{
    async outResultAliasPath(value) {
      await this.checkedAliasNameIsEmptyOrRepeatAndDangerCode(value);
      this.codeBlockConfig.resultAliasPath = value;
    },
    handleCreateAlias() {
      this.$refs.returnParseModal.handleShowModal();
    },
    testCompilerResult(form) {
      // 校验代码块是否有特殊字段
      if(checkJsCode(this.codeBlockConfig.codeContent)){
        return platform.notification({
          title: i18n.t('formSetting.jsCodeBlock.testRunMsg3'),
          message: i18n.t('formSetting.jsCodeBlock.testRunMsg4'),
          type: 'warning',
        });
      }
      this.codeRunResult = executeCode(this.codeBlockConfig.codeContent, form, this.field, true);
      const resultAliasPath = this.convertResultAliasPath();
      this.compilerResult(this.codeRunResult,resultAliasPath);
    },

    /** 编译结果值*/
    compilerResult(code, resultAliasPath = []) {
      this.analysisResults = isString(code) ? code : code.toString();
      this.codeBlockConfig.resultAliasPath = resultAliasPath;
      this.pending = !isObjectString(this.analysisResults);
    },
    /** 生成别名 */
    convertResultAliasPath (){
      if (!isObjectString(this.codeRunResult)) return [];

      const outPutResults = JSON.parse(this.codeRunResult);

      return Object.keys(outPutResults).map(key => {
        const existResultAliasPathItem = this.resultAliasPath.find(item=> item.aliasPath.includes(key));
        if(existResultAliasPathItem) return  { ...existResultAliasPathItem };
        const aliasId = uuid();
        const aliasName = key.length > 50 ? key.substring(0, 50) : key;
        const aliasPath = `$.${key}`;
        const aliasFieldName = this.field.fieldName;

        return { aliasId, aliasName, aliasPath, aliasFieldName };
      });
    },
    checkedAliasNameIsEmptyOrRepeatAndDangerCode (resultAliasPath = this.codeBlockConfig.resultAliasPath) {
      return new Promise((resolve, reject)=> {
        const isNullAliasName = resultAliasPath.some(item => !item.aliasName || item.aliasName.match(/^\s*$/));
        const isRepeatName = resultAliasPath.some((obj, index) => {
          return resultAliasPath.findIndex((item, idx) => idx !== index && item.aliasName === obj.aliasName) !== -1;
        });

        if( isNullAliasName) return this.$message.warning(this.$t('formSetting.jsCodeBlock.aliasNoEmpty'));

        if( isRepeatName ) return this.$message.warning(this.$t('formSetting.jsCodeBlock.aliasNoRepeat'));

        if(checkJsCode(this.codeBlockConfig.codeContent)) {
          return platform.notification({
            title: i18n.t('formSetting.jsCodeBlock.testRunMsg3'),
            message: i18n.t('formSetting.jsCodeBlock.testRunMsg4'),
            type: 'warning',
          });
        }

        resolve();
      });
    },
    /* 打开调试弹出框 */
    testRun() {
      this.$refs.testRunModals.testRunModalOpen(true);
    },
    // 设置编辑器高度
    // setEditorHeight(height) {
    //   const { codemirror } = this.$refs.cmEditor;
    //   codemirror.setSize(null, height);
    // },
    /* 处理字段 */
    handleField(content) {
      console.log("content",content);
      const { codemirror } = this.$refs.cmEditor;
      //获取codemirror光标位置
      const pos = codemirror.getCursor();
      const insertPos = {
        line: pos.line,
        ch: pos.ch
      };
      codemirror.replaceRange(content, insertPos);//先插入字符串
    },
    /** 代码编辑 */
    handleCodemirrorChange() {
      // 获取插入表单字段
      this.insertFields = this.getInsertFields(this.codeBlockConfig.codeContent);
    },
    /** 获取插入的表单字段 */
    getInsertFields(code = '') {
    // 插入表单字段
      let reg =/\$(.*?)\$/g;
      const matches = code.match(reg);
      const insertFields = matches ? matches.map(match => match.slice(1, -1)): [];

      return this.canInsertedFields.filter(item=> insertFields.includes(item.fieldName)) || [];
    },
    /** 检查内容是否有修改 */
    checkModified() {
      const codeBlockConfig = this.field.setting?.codeBlockConfig || {
        automaticOperation: 0,
        codeContent:'', // js代码块
        resultAliasPath:[], // 创建别名
      };

      return JSON.stringify(this.codeBlockConfig) !== JSON.stringify(codeBlockConfig) ;
    },
    handleSave() {
      this.update(this.codeBlockConfig);
      this.visible = false;
    },
    /** 关闭校验内容是否修改 */
    async onClose() {
      // 内容修改
      if(this.checkModified()) {
        const confirm = await this.$confirm(this.$t('view.designer.tip.tip4'), '', { confirmButtonText: this.$t('common.base.save'), cancelButtonText: this.$t('common.base.notSaveForNow'), type: 'warning', showClose: false, closeOnClickModal: false }).catch(() => {});
        if (!confirm){
          const codeBlockConfig = this.field.setting?.codeBlockConfig;
          let beforeValue = null;
          if(codeBlockConfig) {
            beforeValue = codeBlockConfig;
          } else {
            beforeValue = {
              automaticOperation: 0,
              codeContent:'', // js代码块
              resultAliasPath:[], // 创建别名
            };
          }
          this.codeBlockConfig = beforeValue;
          this.visible = false;
          return;
        }
        await this.checkedAliasNameIsEmptyOrRepeatAndDangerCode();

        this.update(this.codeBlockConfig);
      }
      this.visible = false;
    },
    /** 更新数据 */
    update(value) {
      const isSetting = true;
      const prop = 'codeBlockConfig';

      this.$emit('input', value, prop, isSetting);

    },
  },
};
</script>
<style lang="scss">
.compilation-test-modal {
  width:100%;
  margin: auto;
  height: 100vh;
  padding: 0 !important;
  .base-modal-body{
    padding: 0 !important;
  }
  .tab-steps-body{
    padding: 10px;
    width: 760px;
    margin: auto;
    overflow: hidden;
    .tab-steps {
      width: 100%;
      margin: auto;
      padding: 20px 0px 10px 0;
    }
    .tab-step{
      height:450px;
    }
    .vue-codemirror {
      width: 100%;
      border: 1px solid #CCD4E0;
      border-radius: 4px;
      overflow: hidden;
      .CodeMirror {
        height: 360px;
        .CodeMirror-gutters {
          background: #fff;
          border-right:0px solid #ddd;
        }
        .cm-string {
          color: #10C780;
        }
      }
    }
  }
    header {
    display: flex !important;
    color: #fff;
      div {
        cursor: pointer;
        background-color:  $color-primary;
        border-radius: 2px;
        &:nth-child(n) {
          box-sizing: border-box;
          display: flex;
          align-items: center;
          margin-right: 15px;
          //border: 1px solid #ccc;
          height: 25px;
          font-size: 12px;
          padding: 5px 10px;
        }
        span {
          margin-left: 4px;
        }
      }
  }
}
.compilation-test-modal__content{
  main {
    display: grid !important;
    grid-template-columns: 132px 1fr;
    .code-editor-left {
      width: 132px;
      height: calc(100vh - 44px);
      background-color: #424647;
    }
    .code-editor-right {
      height: calc(100vh - 44px);
      background-color: #2B2B2B;
      display: flex;
      flex-direction: column;
      .code-editor-right-top{
        height: calc(100vh - 234px);
        .codemirror {
          height: 100%;
          .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div{
            background: #4E5253;
          }
          .CodeMirror{
            height: 100% !important;
          }
        }
      }
      footer {
        height: 190px;
        background: #151515;
        display: flex;
        flex-direction: column;
        .console-panel{
          height: 40px;
          background-color: #3a3d3e;
          display: flex;
          justify-content: space-between;
          align-items: center;
          p{
            height: 24px;
            line-height: 24px;
            width: 100%;
            color: rgba(255, 255, 255, 0.64);
            padding-left: 8px;
            margin-bottom: 0;
          }
          .console-panel-collapse{
            margin-right: 14px;
            span{
              width: 14px;
              display: inline-block;
              color: #fff;
              border-top: 2px solid #fff;
              border-radius: 4px;
              cursor: pointer;
              margin-bottom: 3px;
            }
          }
          .alias-btn{
            margin-right: 16px;
            button{
              height: 20px;
              padding: 0 10px;
              border-radius: 2px;
              //line-height: 20px;
              i, span{
                display: inline-block;
                //margin-bottom: 4px;
              }
            }
          }
        }
        .test-run-right{
          height: calc(100% - 40px);
          box-sizing: border-box;
          .test-run-inforn{
            height: 100%;
            overflow: auto;
          &::-webkit-scrollbar {
              //width: 10px; /* 设置滚动条的宽度 */
            }

          &::-webkit-scrollbar-track {
              background: transparent; /* 滚动条轨道的背景颜色 */
            }

          &::-webkit-scrollbar-thumb {
              background: #4E5253; /* 滚动条实际可拖动部分的颜色 */
            }

          &::-webkit-scrollbar-thumb:hover {
              background: #555; /* 滚动条可拖动部分的悬停颜色 */
            }
          }
          .test-run-content{
            box-sizing: border-box;
            height: 100%;
            padding: 10px;
          }
          pre{
            color: #bfbfbf;
            //padding: 12px 16px;
            width: 100%;
            // height: 100%;
            // overflow: auto;
            margin-bottom: 0;
            .string {
              color: green;
            }
            .number {
              color: #ed6a0c;
            }
            .boolean {
              color: blue;
            }

            .null {
              color: magenta;
            }
            .key {
              color: #08979c;
            }
          }
          .test-run-error{
            color: red;
          }
        }
      }
    }
  }
}

</style>
